/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package phidgetmotion.gui;

import com.phidgets.event.AttachEvent;
import com.phidgets.event.AttachListener;
import com.phidgets.event.DetachEvent;
import com.phidgets.event.DetachListener;
import java.awt.BorderLayout;
import phidgetmotion.Error;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import javax.swing.filechooser.FileNameExtensionFilter;

import phidgetmotion.classifier.Classifier;
import phidgetmotion.classifier.Model;
import phidgetmotion.classifier.ModelChangeListener;
import phidgetmotion.classifier.UpdateListener;
import phidgetmotion.evaluation.Evaluater;
import phidgetmotion.evaluation.Experiment;
import phidgetmotion.evaluation.User;
import phidgetmotion.gui.util.NamedBoxLayoutPanel;
import phidgetmotion.phidget.Accelerometer;

/**
 *
 * @author monika
 */
public class EvaluatePanel extends JPanel {
	private static final long serialVersionUID = -2874067145492953657L;

	public EvaluatePanel() {
		initComponents();
		Model.addModelChangeListener(modelChangedListener);
		Accelerometer.addAttachListener(attachListener);
		Accelerometer.addDetachListener(detachListener);
	}

	private JTextField userName = new JTextField();
	private JTextField userAge = new JTextField();
	private JRadioButton userMale = new JRadioButton("male");
	private JRadioButton userFemale = new JRadioButton("female");
	private ButtonGroup userGender = new ButtonGroup();
	private JTextField activityFileName = new JTextField();
	private JTextField statisticFileName = new JTextField();

	private JButton saveStatisticFileButton = new JButton("Save");
	private JButton saveAsStatisticFileButton = new JButton("Save As");
	private JButton openStatisticFileButton = new JButton("Open");
	private JButton showStatisticFileButton = new JButton("Show");
	private JButton saveTablesButton = new JButton("Gen Tables");

	private JButton createActivityFileButton = new JButton("Create");
	private JButton openActivityFileButton = new JButton("Open");

	private JButton startEvaluationButton = new JButton("start Evaluation");

	private int evaluatedClassId;
	private JLabel evaluatedActivity = new JLabel(" ");
	private JLabel toEvaluateActivity = new JLabel(" ");
	private JLabel countdownPrepare = new JLabel(" ");
	private JLabel countdownRecognize = new JLabel(" ");

	private EvaluationThread tEval;

	private File activityFile, statisticFile;

	private Evaluater evaluater;
	private User currentUser;
	private Classifier classifier;
	
	private ActionListener saveTablesListener = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent e) {
			String prefix = statisticFile.getName();
			prefix = prefix.substring(0,prefix.length()-4);
			try {
				evaluater.saveTables(prefix);
			} catch (IOException e1) {
				Error.error(e1);
			}
		}
	};

	private void initComponents() {
		setLayout(new BorderLayout());
		add(getEvaluationDataPanel(), BorderLayout.NORTH);
		add(getEvaluationPanel(), BorderLayout.CENTER);
	}

	private JPanel getEvaluationPanel() {
		JLabel prepareCntDwnLbl = new JLabel("Evaluation starts in: ");
		JLabel recognizeCntDwnLbl = new JLabel("Evaluation for: ");

		JPanel ePanel = new NamedBoxLayoutPanel("Evaluation", 0);
		JPanel displayedActPanel = new JPanel();
		JPanel displayedActCntDwnPanel = new JPanel();
		JPanel evaluatedActPanel = new JPanel();
		JPanel evaluatedActCntDwnPanel = new JPanel();
		JPanel buttonPanel = new JPanel();

		ePanel.setLayout(new GridLayout(1,3));

		displayedActCntDwnPanel.setLayout(new BorderLayout());
		displayedActCntDwnPanel.add(prepareCntDwnLbl, BorderLayout.WEST);
		displayedActCntDwnPanel.add(countdownPrepare, BorderLayout.EAST);
		displayedActPanel.setBorder(new TitledBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "Activity"));
		displayedActPanel.setLayout(new BorderLayout());
		displayedActPanel.add(toEvaluateActivity, BorderLayout.CENTER);
		displayedActPanel.add(displayedActCntDwnPanel, BorderLayout.SOUTH);

		evaluatedActCntDwnPanel.setLayout(new BorderLayout());
		evaluatedActCntDwnPanel.add(recognizeCntDwnLbl, BorderLayout.WEST);
		evaluatedActCntDwnPanel.add(countdownRecognize, BorderLayout.EAST);
		evaluatedActPanel.setBorder(new TitledBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "recognized Activity"));
		evaluatedActPanel.setLayout(new BorderLayout());
		evaluatedActPanel.add(evaluatedActivity, BorderLayout.CENTER);
		evaluatedActPanel.add(evaluatedActCntDwnPanel, BorderLayout.SOUTH);

		startEvaluationButton.addActionListener(startStopEvaluationListener);
		startEvaluationButton.setEnabled(false);
		buttonPanel.add(startEvaluationButton);

		ePanel.add(displayedActPanel);
		ePanel.add(evaluatedActPanel);
		ePanel.add(buttonPanel);
		
		return ePanel;
	}

	private JPanel getEvaluationDataPanel() {
		JPanel panel = new NamedBoxLayoutPanel("Evaluation Data", BoxLayout.Y_AXIS);
		JPanel userPanel = new JPanel();
		JPanel statisticPanel = new JPanel();
		JPanel activityPanel = new JPanel();

		Dimension buttonDimension = new Dimension(90, 25);

		userPanel.setLayout(new BoxLayout(userPanel, BoxLayout.X_AXIS));
		userPanel.add(new JLabel("Username: "));
		userName.setMaximumSize(new Dimension(Integer.MAX_VALUE, 25));
		userName.setMinimumSize(new Dimension(100, 25));
		userName.setPreferredSize(new Dimension(100, 25));
		userName.setEditable(true);
		userPanel.add(userName);
		userAge.setMaximumSize(new Dimension(35, 25));
		userAge.setMinimumSize(new Dimension(35, 25));
		userAge.setPreferredSize(new Dimension(35, 25));
		//TODO: userAge.setInputVerifier(null);
		userPanel.add(new JLabel("Age: "));
		userPanel.add(userAge);
		userGender.add(userMale);
		userGender.add(userFemale);
		userPanel.add(userMale);
		userPanel.add(userFemale);
		userPanel.add(Box.createHorizontalGlue());

		activityPanel.setLayout(new BoxLayout(activityPanel, BoxLayout.X_AXIS));
		activityPanel.add(new JLabel("Activities: "));
		activityFileName.setMaximumSize(new Dimension(Integer.MAX_VALUE, 25));
		activityFileName.setMinimumSize(new Dimension(100, 25));
		activityFileName.setPreferredSize(new Dimension(100, 25));
		activityFileName.setEditable(false);
		activityPanel.add(activityFileName);
		activityPanel.add(Box.createHorizontalGlue());

		createActivityFileButton.setMinimumSize(buttonDimension);
		createActivityFileButton.setPreferredSize(buttonDimension);
		createActivityFileButton.setMaximumSize(buttonDimension);
		createActivityFileButton.addActionListener(createActivityFileListener);
		activityPanel.add(createActivityFileButton);

		openActivityFileButton.setMinimumSize(buttonDimension);
		openActivityFileButton.setPreferredSize(buttonDimension);
		openActivityFileButton.setMaximumSize(buttonDimension);
		openActivityFileButton.addActionListener(openActivityFileListener);
		activityPanel.add(openActivityFileButton);

		statisticPanel.setLayout(new BoxLayout(statisticPanel, BoxLayout.X_AXIS));
		statisticPanel.add(new JLabel("Statistic: "));
		statisticFileName.setMaximumSize(new Dimension(Integer.MAX_VALUE, 25));
		statisticFileName.setMinimumSize(new Dimension(100, 25));
		statisticFileName.setPreferredSize(new Dimension(300, 25));
		statisticFileName.setEditable(false);
		statisticPanel.add(statisticFileName);
		statisticPanel.add(Box.createHorizontalGlue());

		showStatisticFileButton.setMinimumSize(buttonDimension);
		showStatisticFileButton.setPreferredSize(buttonDimension);
		showStatisticFileButton.setMaximumSize(buttonDimension);
		showStatisticFileButton.addActionListener(showStatisticFileListener);
		showStatisticFileButton.setEnabled(false);
		statisticPanel.add(showStatisticFileButton);
		
		saveTablesButton.setMinimumSize(buttonDimension);
		saveTablesButton.setPreferredSize(buttonDimension);
		saveTablesButton.setMaximumSize(buttonDimension);
		saveTablesButton.addActionListener(saveTablesListener);
		saveTablesButton.setEnabled(false);
		statisticPanel.add(saveTablesButton);
		
		openStatisticFileButton.setMinimumSize(buttonDimension);
		openStatisticFileButton.setPreferredSize(buttonDimension);
		openStatisticFileButton.setMaximumSize(buttonDimension);
		openStatisticFileButton.addActionListener(openStatisticFileListener);
		statisticPanel.add(openStatisticFileButton);

		saveAsStatisticFileButton.setMinimumSize(buttonDimension);
		saveAsStatisticFileButton.setPreferredSize(buttonDimension);
		saveAsStatisticFileButton.setMaximumSize(buttonDimension);
		saveAsStatisticFileButton.addActionListener(saveAsStatisticFileListener);
		statisticPanel.add(saveAsStatisticFileButton);

		saveStatisticFileButton.setMinimumSize(buttonDimension);
		saveStatisticFileButton.setPreferredSize(buttonDimension);
		saveStatisticFileButton.setMaximumSize(buttonDimension);
		saveStatisticFileButton.addActionListener(saveStatisticFileListener);
		statisticPanel.add(saveStatisticFileButton);

		panel.add(userPanel);
		panel.add(activityPanel);
		panel.add(statisticPanel);
		return panel;
	}

	private class EvaluationThread extends Thread {
		private EvaluationTimerThread tEvaluationTimer = null;
		private static final long CNTDWN_INTERVAL = 1000;
		private static final int PREP_COUNTDOWN = 4;
		private static final int EVAL_COUNTDOWN = 10;

		private long evaluationTimeStart, evaluationTimeEnd;
		int currentActivity = -1;
		boolean currentActivityRecognized = false;
		final Object monitor = new Object();

		public void setEvaluationTimeStart() {
			this.evaluationTimeStart = System.currentTimeMillis();
		}

		public long getEvaluationTimeStart() {
			return this.evaluationTimeStart;
		}

		public long getEvaluationTimeEnd() {
			return this.evaluationTimeEnd;
		}

		public void resetEvaluationTimeEnd() {
			this.evaluationTimeEnd = -1;
		}

		public void setEvaluationTimeEnd() {
			if (this.evaluationTimeEnd < 0)
				this.evaluationTimeEnd = System.currentTimeMillis();
		}

		public boolean isCurrentActivityRecognized() {
			synchronized(monitor) {
				return currentActivityRecognized;
			}
		}

		public void setCurrentActivityRecognized(boolean recognized) {
			synchronized(monitor) {
				currentActivityRecognized = recognized;
				if (recognized) {
					setEvaluationTimeEnd();
				}
			}
		}

		@Override
		public void run() {
			ArrayList<Experiment> activities = evaluater.getActivities();
			if (activities != null) {
				for (int i = 0; !isInterrupted() && i < activities.size(); i++) {
					currentActivity = activities.get(i).getActivity();
					setCurrentActivityRecognized(false);

					if (currentActivity < classifier.getClasses().length) {
						String activity = classifier.getClasses()[currentActivity];
						evaluatedActivity.setText("");
						toEvaluateActivity.setText(activity);
						Thread t = new PreparationCountdownThread(PREP_COUNTDOWN);
						t.start();
						try {
							t.join();
						} catch (InterruptedException ex) {
							t.interrupt();
							return;
						}

						evaluatedActivity.setVisible(true);
						tEvaluationTimer = new EvaluationTimerThread(EVAL_COUNTDOWN);
						resetEvaluationTimeEnd();
						setEvaluationTimeStart();
						tEvaluationTimer.start();
						try {
							tEvaluationTimer.join();
						} catch (InterruptedException ex) {
							tEvaluationTimer.interrupt();
							return;
						}
						setEvaluationTimeEnd();

						Experiment experiment = new Experiment(currentActivity);
						experiment.setEvaluatedActivity(evaluatedClassId);
						long recognTime = (currentActivity == evaluatedClassId) ? getEvaluationTimeEnd()-getEvaluationTimeStart() : -1;
						experiment.setRecognitionTime(recognTime);
						currentUser.addExperiment(experiment);
						try {
							Thread.sleep(CNTDWN_INTERVAL*3);
						} catch (InterruptedException e) {
							//e.printStackTrace();
							//Error.error(e);
							interrupt();
							//return;
						}

						countdownRecognize.setText("");
						evaluatedActivity.setVisible(false);
						evaluatedActivity.setText("");
					}
				}
			}
			startEvaluationButton.setText("start Evaluation");
			toEvaluateActivity.setText("Evaluation finished");
		}

		private void evaluateInput(int inputActivity) {
			synchronized(monitor) {
				setCurrentActivityRecognized(isCurrentActivityRecognized() || (currentActivity == inputActivity));
				if (evaluatedClassId < 0)
					evaluatedActivity.setText("not recognized");
				else
					evaluatedActivity.setText(classifier.getClasses()[evaluatedClassId]);
			}
		}

		private class PreparationCountdownThread extends Thread {
			private int countdown = -1;

			public PreparationCountdownThread(int countdown) {
				this.countdown = countdown;
			}

			@Override
			public void run() {
				while (countdown > 0) {
					countdownPrepare.setText(Integer.toString(countdown));
					try {
						Thread.sleep(CNTDWN_INTERVAL);
					} catch (InterruptedException ex) {
						countdownPrepare.setText("");
						interrupt();
						return;
					}
					countdown--;
				}
				countdownPrepare.setText("");
			}
		};

		private class EvaluationTimerThread extends Thread {
			private int timer = -1;

			public EvaluationTimerThread(int seconds) {
				this.timer = seconds;
			}

			@Override
			public void run() {
				while (timer > 0 && !isCurrentActivityRecognized()) {
					countdownRecognize.setText(Integer.toString(timer));
					try {
						Thread.sleep(CNTDWN_INTERVAL);
					} catch (InterruptedException ex) {
						countdownRecognize.setText("");
						interrupt();
						return;
					}
					timer--;
				}
				countdownRecognize.setText(Integer.toString(timer));
			}
		};

	};

	// Listeners
	ActionListener startStopEvaluationListener = new ActionListener() {
		boolean isRunning = false;

		public synchronized void actionPerformed(ActionEvent arg0) {
			isRunning = !isRunning;
			toEvaluateActivity.setText("");
			evaluatedActivity.setText("");
			evaluatedActivity.setEnabled(true);
			countdownPrepare.setText("");
			countdownRecognize.setText("");
			startEvaluationButton.setText("start Evaluation");

			if (tEval != null && tEval.isAlive()) {
				// tEval is running and button stopEvaluation was pressed
				tEval.interrupt();
				try {
					tEval.join();
				} catch (InterruptedException ex) {
					tEval.interrupt();
				}
				tEval = null;
				startEvaluationButton.setText("start Evaluation");
			} else {
				startEvaluationButton.setText("stop Evaluation");
				setUserData();
				tEval = new EvaluationThread();
				tEval.start();
			}
/*
			if (isRunning) {
				startEvaluationButton.setText("stop Evaluation");
				setUserData();
				tEval = new EvaluationThread();
				tEval.start();
			} else {
				if (tEval != null) {
					tEval.interrupt();
					try {
						tEval.join();
					} catch (InterruptedException ex) {
						tEval.interrupt();
					}
					tEval = null;
				}
				startEvaluationButton.setText("start Evaluation");
			}
*/
		}
		private void setUserData() {
			currentUser = evaluater.getUser(userName.getText(), classifier);
			currentUser.setUsername(userName.getText());
			if (userAge.getText().length() > 0)
				currentUser.setAge(Integer.parseInt(userAge.getText()));
			currentUser.setIsMale(userMale.isSelected());
		}
	};
	/*ActionListener startEvaluationListener = new ActionListener() {

		public synchronized void actionPerformed(ActionEvent arg0) {
			if (tEval != null) {
				tEval.interrupt();
				try {
					tEval.join();
				} catch (InterruptedException ex) {
					tEval.interrupt();
					return;
				}
			}
			setUserData();
			tEval = new EvaluationThread();
			tEval.start();

			startEvaluationButton.addActionListener(stopEvaluationListener);
			startEvaluationButton.removeActionListener(this);
			toEvaluateActivity.setText("");
			evaluatedActivity.setText("");
			evaluatedActivity.setEnabled(true);
			countdownPrepare.setText("");
			countdownRecognize.setText("");
			startEvaluationButton.setText("stop Evaluation");
		}
		private void setUserData() {
			currentUser = evaluater.getUser(userName.getText(), classifier);
			currentUser.setUsername(userName.getText());
			if (userAge.getText().length() > 0)
				currentUser.setAge(Integer.parseInt(userAge.getText()));
			currentUser.setIsMale(userMale.isSelected());
		}
	};

	ActionListener stopEvaluationListener = new ActionListener() {

		public synchronized void actionPerformed(ActionEvent arg0) {
			if (tEval != null) {
				tEval.interrupt();
				try {
					tEval.join();
				} catch (InterruptedException ex) {
					tEval.interrupt();
					return;
				}
			}
			try {
				startEvaluationButton.addActionListener(startEvaluationListener);
				startEvaluationButton.removeActionListener(this);
			} catch (Exception e) {
				e.printStackTrace();
			}
			toEvaluateActivity.setText("Evaluation stopped");
			evaluatedActivity.setText("");
			evaluatedActivity.setEnabled(false);
			countdownPrepare.setText("");
			countdownRecognize.setText("");
			startEvaluationButton.setText("start Evaluation");
		}
	};*/

	UpdateListener updateListener = new UpdateListener() {

		public void update(int classId) {
			evaluatedClassId = classId;
			if (tEval != null)
				tEval.evaluateInput(classId);
		}

	};

	ModelChangeListener modelChangedListener = new ModelChangeListener() {

		public void modelAdded(Model model) {
			classifier = model;
			classifier.addUpdateListener(updateListener);
			evaluater = new Evaluater(classifier);
			statisticFile = null;
			activityFile = null;
			refreshGUI();
		}

		public void modelRemoved() {
			if (tEval != null) tEval.interrupt();
		}

	};

	ActionListener createActivityFileListener = new ActionListener() {

		public void actionPerformed(ActionEvent ae) {
			JFileChooser fileChooser = new JFileChooser();
			fileChooser.setCurrentDirectory(new File("."));
			fileChooser.setMultiSelectionEnabled(false);
			fileChooser.setFileFilter(new FileNameExtensionFilter("Activity files", "pma"));

			if (fileChooser != null && fileChooser.showSaveDialog(EvaluatePanel.this) == JFileChooser.APPROVE_OPTION) {
				activityFile = fileChooser.getSelectedFile();
				activityFile = new File(activityFile.getName());
				try {
					if (!activityFile.exists()) {
						activityFile = new File(activityFile.getPath());
						activityFile.createNewFile();
					}
					evaluater.saveActivities(activityFile, 6);
					evaluater.loadActivities(activityFile);
				}  catch (IOException e) {
					Error.error(e);
				}
				refreshGUI();
			}
		}

	};

	ActionListener openActivityFileListener = new ActionListener() {

		public void actionPerformed(ActionEvent ae) {
			JFileChooser fileChooser = new JFileChooser();
			fileChooser.setCurrentDirectory(new File("."));
			fileChooser.setMultiSelectionEnabled(false);
			fileChooser.setFileFilter(new FileNameExtensionFilter("Activity files", "pma"));

			if (fileChooser != null && fileChooser.showOpenDialog(EvaluatePanel.this) == JFileChooser.APPROVE_OPTION) {
				activityFile = fileChooser.getSelectedFile();
				activityFileName.setText(activityFile.getName());
				try {
					evaluater.loadActivities(activityFile);
				}  catch (IOException e) {
					Error.error(e);
				}
				refreshGUI();
			}
		}
	};

	ActionListener showStatisticFileListener = new ActionListener() {

		public void actionPerformed(ActionEvent ae) {
			if (statisticFile != null)
				evaluater.showStatistics(statisticFile);
		}
	};

	ActionListener openStatisticFileListener = new ActionListener() {

		public void actionPerformed(ActionEvent arg0) {
			JFileChooser fileChooser = new JFileChooser();
			fileChooser.setCurrentDirectory(new File("."));
			fileChooser.setMultiSelectionEnabled(false);
			fileChooser.setFileFilter(new FileNameExtensionFilter("Statistic files", "pms"));

			if (fileChooser != null && fileChooser.showOpenDialog(EvaluatePanel.this) == JFileChooser.APPROVE_OPTION) {
				statisticFile = fileChooser.getSelectedFile();
				statisticFileName.setText(statisticFile.getName());
				try {
					if (!statisticFile.exists()) {
						statisticFile = new File(statisticFile.getPath());
						statisticFile.createNewFile();
					}
					evaluater.openStatistics(statisticFile);
				} catch (IOException e) {
					Error.error(e);
				} catch (ClassNotFoundException e) {
					Error.error(e);
				}
				refreshGUI();
			}
		}
	};

	ActionListener saveStatisticFileListener = new ActionListener() {

		public void actionPerformed(ActionEvent ae) {
			if (statisticFile != null) {
				try {
					evaluater.saveStatistics(statisticFile);
				} catch (IOException e) {
					Error.error(e);
				}
				refreshGUI();
			}
			else
				saveAsStatisticFileListener.actionPerformed(ae);
		}
	};

	ActionListener saveAsStatisticFileListener = new ActionListener() {

		public void actionPerformed(ActionEvent ae) {
			JFileChooser fileChooser = new JFileChooser();
			fileChooser.setCurrentDirectory(new File("."));
			fileChooser.setMultiSelectionEnabled(false);
			fileChooser.setFileFilter(new FileNameExtensionFilter("Statistic files", "pms"));

			if (fileChooser != null && fileChooser.showSaveDialog(EvaluatePanel.this) == JFileChooser.APPROVE_OPTION) {
				statisticFile = fileChooser.getSelectedFile();
				try {
					if (!statisticFile.exists())
						statisticFile.createNewFile();
					evaluater.saveStatistics(statisticFile);
				} catch (IOException e) {
					Error.error(e);
				}
				refreshGUI();
			}
		}
	};

	AttachListener attachListener = new AttachListener() {
		@Override
		public void attached(AttachEvent arg0) {
			refreshGUI();
		}
	};

	private DetachListener detachListener = new DetachListener() {
		@Override
		public void detached(DetachEvent arg0) {
			if (tEval != null)
				tEval.interrupt();
			refreshGUI();
		}
	};

	private void refreshGUI() {
		if (activityFile != null && Accelerometer.isConnected()) {
			startEvaluationButton.setEnabled(true);
			startEvaluationButton.setText("start Evaluation");
		}
		if (activityFile != null) activityFileName.setText(activityFile.getName());
		else {
			activityFileName.setText("");
			startEvaluationButton.setEnabled(false);
		}
		if (statisticFile != null) {
			statisticFileName.setText(statisticFile.getName());
			showStatisticFileButton.setEnabled(true);
			saveTablesButton.setEnabled(true);
		}
		else{
			statisticFileName.setText("");
			showStatisticFileButton.setEnabled(false);
			saveTablesButton.setEnabled(false);
		}
		if (!Accelerometer.isConnected()) {
			startEvaluationButton.setEnabled(false);
		}
	}

}
